package org.ninjarope.cinderstained.main.gameplay.definition.towerscript;

import java.io.IOException;

import org.ninjarope.cinderstained.main.engine.Game;
import org.ninjarope.cinderstained.main.engine.map.GameMap;
import org.ninjarope.cinderstained.main.gameplay.Summon;
import org.ninjarope.cinderstained.main.gameplay.definition.Stat;

/**
 * TowerScriptParser
 * 
 * parses scripts and creates objects
 * 
 * @author miroslav
 * 
 */
public class TowerScriptParser {
	GameMap loader;

	String token = "";
	TowerScriptTokenizer t;

	String name = "";

	/**
	 * Executes a script
	 * 
	 * @param filename
	 *            String filename of the script
	 * @throws IOException
	 *             exception thrown if input is not expected
	 */
	public void execute(GameMap map, String filename) throws IOException {
		loader = map;
		t = new TowerScriptTokenizer(filename);
		while (t.more()) {
			token = t.next();
			if (token.equalsIgnoreCase("summon")) {
				parseSummon();
			}
		}
	}

	/**
	 * Parses a summon statement block
	 * 
	 * @throws IOException
	 */
	protected void parseSummon() throws IOException {
		t.need(" ");
		t.need("\"");
		name = "";
		do {
			if (t.get().equals("\""))
				break;
			name += t.get();
		} while (!t.expect("\""));

		Summon actor = (Summon) Game.get().getPool("summons").request();
		loader.summonsDef.def.put(name, actor);
		t.need(" ");
		t.need("{");
		parseSummonBlock(actor);
	}

	/**
	 * Parses a internal summon block.
	 * 
	 * @throws IOException
	 */
	protected void parseSummonBlock(Summon actor) throws IOException {
		// can start with "is", "has", "default", "on" or numeral
		t.next();

		token = t.next();
		if (token.equalsIgnoreCase("is")) {
			parseSummonBlockIs(actor);
		} else if (token.equalsIgnoreCase("range")) {
			parseStat(actor.range);
			t.need(";");
		} else if (token.equalsIgnoreCase("health")) {
			parseStat(actor.health);
			t.need(";");
		} else if (token.equalsIgnoreCase("attack")) {
			t.need(" ");
			t.need("speed");
			t.need(" ");
			parseStat(actor.attackSpeed);
			t.need(";");
		} else if (token.equalsIgnoreCase("damage")) {			
			t.need(" ");
			token = t.next();
			actor.dmg.type = token;
			t.need(" ");
			parseStat(actor.dmg);
			if(!t.expect(";")) {
				t.need(" ");
				t.need("against");
				t.need(" ");
				token = t.next();
				actor.dmg.against = token;
				t.need(";");
			}
		} else if (token.equalsIgnoreCase("has")) {
		} else if (token.equalsIgnoreCase("default")) {
		} else if (token.equalsIgnoreCase("on"));
	}
	
	/**
	 * Resolves the "is" statement of a summon
	 * @param actor summon in question
	 * @throws IOException
	 */
	protected void parseSummonBlockIs(Summon actor) throws IOException {
		while (!t.expect(";")) {
			if (!t.more())
				break;
			token = t.get();
			if(token.equals(";")) break;
			if (!token.equalsIgnoreCase(" ")) {
				actor.set(token);					
			}
		}
	}
	
	/**
	 * Parses a generic stat in the form <code>_NUM_[(NUM[%])];</code>
	 * @param s Stat stat to be generated
	 * @throws IOException
	 */
	protected void parseStat(Stat s) throws IOException {
		t.expect(" ");
		token = t.next();
		try {
			int v = Integer.parseInt(token);
			s.base = v;
			s.current = v;
		} catch(NumberFormatException e) {
			throw new IOException("number expected in RANGE, " + token + "found.");
		}
		token = t.next();
		if(token.equals(" ")) {
			token = t.next();
			if(t.expect("(")) {
				try {
					int v = Integer.parseInt(t.next());
					s.up = v;
					if(s.percent = t.expect("%")) t.expect(")");
				} catch(NumberFormatException e) {
					throw new IOException("number expected in RANGE, " + token + "found.");
				}
				t.need(";");
			} else {
				throw new IOException("( expected, " + token + " found.");
			}
		}
	}
}
